package com.java.thread;

/**
 * 生产者消费者模式
 * @author jack
 */
public class TestProducerConsumerPattern {
	public static void main(String[] args) {
		Store s=new Store(10);
		Producer p1=new Producer(s);
		Consumer c1=new Consumer(s);
		p1.setName("生产者线程 ");
		c1.setName("消费者线程 ");
		p1.start();
		c1.start();
	}
	

}

class Producer extends Thread {
	private Store s;
	public Producer(Store s){
		this.s=s;
	}
	public void run(){
		while(true){
			s.addData();
			//下面一行进行测试是不妥的，对count的访问应该在原子操作里addData()，因为可能在add之后此输出之前又消费了，会产生输出混乱 
			System.out.println("生产了数据："+s.getCount());
			try {
				Thread.sleep(300);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}


class Consumer extends Thread{
	private Store s;
	
	public Consumer(Store s){
		this.s=s;
	}
	
	public void run(){
		while(true){
			s.removeData();
			//下面一行进行测试也是不妥的，对count的访问应该在原子操作里removeData()，因为可能在remove之后此输出之前又生产了，会产生输出混乱 
			System.out.println("消费了数据："+s.getCount());
			try {
				Thread.sleep(600);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}


class Store {
	private int count;
	private final int SIZE;

	public Store(int size){
		this.SIZE=size;
		count=0;
	}
	
	//由生产者线程来做,生产者和消费者共享仓库(Store对象)
	public synchronized void addData(){
		/*
		 如果仓库满了，就要让生产者等待，进入等待池中，并释放当前对象的锁
		wait() 和 notify() 两个方法配套使用，并且这一对方法调用必须放置在 synchronized 方法或块中，该方法或块的上锁对象就是调用这一对方法的对象。若不满足这一条件，则程序虽然仍能编译，但在运行时会出现IllegalMonitorStateException 异常
		原因：只有在synchronized 方法或块中当前线程才占有对象锁，这也是wait() 和 notify() 两个方法属于Object类的方法的原因
		wait() 和 notify() 方法的上述特性决定了它们经常和synchronized 方法或块一起使用，操作系统的进程间通信机制作相似
		*/
		while(count==this.SIZE){
//			Thread.currentThread().sleep(300);//睡觉不会放锁
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		count++;
		System.out.println(Thread.currentThread().getName()+"添加了数据："+count);
		//调用 notifyAll() 方法将把因调用该对象的 wait() 方法而阻塞的所有线程一次性全部解除阻塞。当然，只有获得锁的那一个线程才能进入可运行状态。
		this.notifyAll();
	}
	//由消费者线程来做
	public synchronized void removeData(){
		if(count==0){
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println(Thread.currentThread().getName()+"删除了数据："+count);
		count--;
		this.notifyAll();
	}
	
	public int getCount(){
		return this.count;
	}
}
